#include "stdafx.h"
#include "CXSegmentSet.h"
#include "CXComp.h"
#include "Claxa.h"
#include "Resource.h"

#include <algorithm>
#include <xtd/functional>
#include <xml/stream.h>
#include <MyControls/SegmentActions.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

BEGIN_MESSAGE_MAP(CXSegmentSet, My::SegmentSetCmdImpl)
	ON_COMMAND(ID_SEGMENT_NEW, OnSegmentNew)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_NEW, OnUpdateSegmentNew)
	ON_COMMAND(ID_SEGMENT_NEW_FROM_ACTIVE, OnSegmentNewFromActive)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_NEW_FROM_ACTIVE, OnUpdateSegmentNewFromActive)
	ON_COMMAND(ID_SEGMENT_DELETE, OnSegmentDelete)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_DELETE, OnUpdateSegmentDelete)
	ON_COMMAND(ID_SEGMENT_CANCELALL, OnSegmentCancelAll)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_CANCELALL, OnUpdateSegmentCancelAll)
	ON_COMMAND(ID_SEGMENT_PROCESSALL, OnSegmentProcessAll)
	ON_UPDATE_COMMAND_UI(ID_SEGMENT_PROCESSALL, OnUpdateSegmentProcessAll)
END_MESSAGE_MAP()

CXSegmentSet::CXSegmentSet(CXComp & comp)
	: m_Comp(comp)
{
	Attach(this); //self-attach
}

CXSegmentSet::~CXSegmentSet()
{
	Clear();
}

void CXSegmentSet::Clear()
{
	if (GetDoc().GetSelection().Is<My::SegmentSelection>())
		GetDoc().GetSelection().Clear();
	std::vector<CXSegment *> tmp;
	tmp.swap(m_Segments);
	DispatchEvent(My::SegmentSet::Change(* this));
	std::for_each(tmp.begin(), tmp.end(), xtd::deleter());
}

My::Doc & CXSegmentSet::GetDoc()
{
	return m_Comp;
}

bool CXSegmentSet::IsEmpty() const
{
	return m_Comp.IsEmpty();
}

Size CXSegmentSet::GetSegmentCount() const
{
	return m_Segments.size();
}

CXSegment & CXSegmentSet::GetSegment(Size i)
{
	ASSERT(i < GetSegmentCount());
	return * m_Segments[i];
}

/*void CXSegmentSet::SetActiveSegment(int i)
{
	if (i != -1) {
		if (i < 0)
			i = 0;
		if (i > int(GetSegmentCount()) - 1)
			i = int(GetSegmentCount()) - 1;
	}
	if (i != GetActiveSegment())
	{
		if (GetActiveSegment() >= 0) {
			// Deactivate?
			m_Segments[GetActiveSegment()]->DispatchEvent(
				My::Segment::StateChange(* m_Segments[GetActiveSegment()]));
		}
		GetActiveSegment() = i;

		if (GetActiveSegment() >= 0) {
			// Activate ?
			m_Segments[GetActiveSegment()]->DispatchEvent(
				My::Segment::StateChange(* m_Segments[GetActiveSegment()]));
		}

		DispatchEvent(ActiveSegmentChange(* this));
	}
}
*/

/*
void CXSegmentSet::DeleteActive()
{
	if (GetActiveSegment() == -1)
		return;
	m_Comp.Perform(new My::DeleteSegment<CXSegment>(* this, 
		GetActiveSegment()));
}
*/

void CXSegmentSet::MoveSelection(int index)
{
	if (GetActiveSegment() == -1)
		return;
	m_Comp.Perform(new My::MoveSegment<CXSegment>(* this, 
		GetActiveSegment(), index));
}

CXSegment * CXSegmentSet::GetActiveSegmentPtr()
{
	return GetActiveSegment() == -1 ? 0 : m_Segments[GetActiveSegment()];
}

void CXSegmentSet::Insert(Size i, CXSegment * ptr)
{
	m_Segments.insert(m_Segments.begin() + i, ptr);
}

CXSegment * CXSegmentSet::Erase(Size i)
{
	CXSegment * ptr = m_Segments[i];
	m_Segments.erase(m_Segments.begin() + i);
	return ptr;
}

void CXSegmentSet::OnSegmentNew()
{
	m_Comp.Perform(new My::CreateSegment<CXSegment>(* this,
		new CXSegment(* this, m_Comp.GetCurrentFrame())));
}

void CXSegmentSet::OnUpdateSegmentNew(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!IsEmpty());
}

void CXSegmentSet::OnSegmentNewFromActive()
{
	m_Comp.Perform(new My::CreateSegment<CXSegment>(* this,	
		new CXSegment(* this, *GetActiveSegmentPtr(), m_Comp.GetCurrentFrame())));
}

void CXSegmentSet::OnUpdateSegmentNewFromActive(CCmdUI *pCmdUI)
{
	CXSegment * pSeg = GetActiveSegmentPtr();
	pCmdUI->Enable(pSeg && pSeg->HasBitMask(m_Comp.GetCurrentFrame()));
}

CXSegment * CXSegmentSet::GetTopmostSegmentPtr(Size Frame) const
{
	for (Size n = 0; n < m_Segments.size(); ++n) {
		CXSegment * pSeg = m_Segments[n];
		if (pSeg->GetActiveDataSpan().Inside(Frame))
			return pSeg;
	}
	return 0;
}

CXSegment * CXSegmentSet::GetTopmostNotNoneSegmentPtr(Size Frame) const
{
	for (Size n = 0; n < m_Segments.size(); ++n) {
		CXSegment * pSeg = m_Segments[n];
		if (pSeg->GetActiveDataSpan().Inside(Frame) && pSeg->GetMaskMode().mode != MaskMode::None)
			return pSeg;
	}
	return 0;
}

bool CXSegmentSet::IsSomewhereTopmost(Size SegmentIndex) const
{
	assert (SegmentIndex < m_Segments.size());
	CXSegment const & Seg = *m_Segments[SegmentIndex];
	Span s = Seg.GetActiveDataSpan();
	if (s.Empty())
		return false;
	if (SegmentIndex == 0)
		return true;

	std::vector<Size> topmost(s.Size(), SegmentIndex);
	for (Size i = 0; i < SegmentIndex; ++i) {
		CXSegment const & Seg2 = *m_Segments[i];
		Span s2 = Seg.GetActiveDataSpan();
		s2.intersect(s);
		if (s2.Empty())
			continue;
		s2.Offset(-s.first);
		std::fill(topmost.begin() + s2.first, topmost.begin() + s2.last, i);
	}

	return std::find(topmost.begin(), topmost.end(), SegmentIndex) != topmost.end();
}

BOOL CXSegmentSet::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
	if (GetActiveSegment() != -1) {
//		m_Segments[GetActiveSegment()]->OnCmdMsg(50046, 0, 0, 0);
		if (m_Segments[GetActiveSegment()]->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
			return TRUE;
	}
	return My::SegmentSetCmdImpl::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

void CXSegmentSet::OnSegmentDelete()
{
	My::GetSelection().Erase();
}

void CXSegmentSet::OnUpdateSegmentDelete(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(GetActiveSegment() != -1);
}

xml::stream & CXSegmentSet::Serialize(xml::stream & s)
{
	using namespace xml;
	if (s.out()) {
		for (Size i = 0; i != m_Segments.size(); ++i)
			s.child("Segment") || *m_Segments[i];
	}
	else {
		//in
		assert(m_Segments.empty()); //Don't Clear() here, it may send events

		const_node_list list;
		s.root().children(list, "Segment");
		m_Segments.reserve(list.size());
		const_node_list::const_iterator l;
		int i;
		for (l = list.begin(), i = 0; l != list.end(); ++l) {
			std::auto_ptr<CXSegment> ptr(new CXSegment(* this));
			stream(*l) || * ptr;
			if (ptr->Valid())
				m_Segments.push_back(ptr.release());
		}

		DispatchEvent(Change(* this));
	}
	return s;
}

void CXSegmentSet::OnSegmentCancelAll()
{
	theApp.m_Processor.Cancel();
}

void CXSegmentSet::OnUpdateSegmentCancelAll(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!IsEmpty() && !theApp.m_Processor.IsIdle());
}

void CXSegmentSet::OnSegmentProcessAll()
{
	bool started = false;
	for (unsigned int i = 0; i < m_Segments.size(); ++i)
		started = m_Segments[i]->StartProcessing() || started;
	//if (started)
	//	GetComp().GetView().StartFollow();
}

void CXSegmentSet::OnUpdateSegmentProcessAll(CCmdUI *pCmdUI)
{
	for (unsigned int i = 0; i < m_Segments.size(); ++i)
		if (m_Segments[i]->CanStartProcessing()) {
			pCmdUI->Enable(true);
			return;
		}
	pCmdUI->Enable(false);
}

void CXSegmentSet::ListMasksWithCombMode(Size Frame, bool SmoothMask, MasksCollection & List) const
{
	int s;
	// search the first segment from the top with Substitute mask mode
	for (s = 0; s < m_Segments.size(); ++s) {
		CXSegment const & seg = *m_Segments[s];
		if (!seg.GetTimeSpan().Inside(Frame))
			continue; //there can be mask outside time span
		if (SmoothMask && !seg.HasGreyMask(Frame) || !SmoothMask && !seg.HasBitMask(Frame))
			continue;
		if (seg.GetMaskMode().mode == MaskMode::Substitute)
			break;
	}
	if (s == m_Segments.size())
		--s;

	// go bottom to top
	for (; s >= 0; --s) {
		CXSegment const & seg = *m_Segments[s];
		if (seg.GetMaskMode().mode == MaskMode::None)
			continue;
		if (!seg.GetTimeSpan().Inside(Frame))
			continue; //there can be mask outside time span
		if (SmoothMask && !seg.HasGreyMask(Frame) || !SmoothMask && !seg.HasBitMask(Frame))
			continue;
		List.push_back(seg.GetMaskWithCombMode(Frame));
	}
}

void CXSegmentSet::ProcessEvent(My::Event & e)
{
	/*if (e.Is<My::Segment::TimeSpanChange>() ||
		e.Is<My::Segment::DataSpanChange>() ||
		e.Is<My::SegmentSet::Event>())
	{
		//InvalidateMergeInfo();
		return;
	}*/
}

bool CXSegmentSet::AreThereSegmentsToProcess() const
{
	for (unsigned int i = 0; i < m_Segments.size(); ++i)
		if (m_Segments[i]->IsProcessed())
			return true;
	return false;
}

My::Sequence * CXSegmentSet::GetSequence() 
{ 
	return &m_Comp; 
}
